from dao import create_db, remember_dao, user_dao
from utils import gen_md5, gen_random_string
from x_com import xport_core
from x_com import source_api
from sqlalchemy.ext.asyncio import create_async_engine
from databases import Database
import asyncio
# 自定义异常类，用于表示用户相关的特定错误情况
class UserExistsError(Exception):
    """当尝试添加已存在的用户时抛出此异常"""
    pass

class UserNotFoundError(Exception):
    """当查找的用户不存在时抛出此异常"""
    pass

class PasswordError(Exception):
    """当密码验证不通过时抛出此异常"""
    pass

class UserService:
    # 设置 sqlite 的连接方式和存放地址
    DATABASE_URL = "sqlite+aiosqlite:///./user.db"
    database = Database(DATABASE_URL)
    engine = create_async_engine(DATABASE_URL, echo=True)
    # 当前登录用户的id
    user_id = 0

    async def init_db(self):
        """
        初始化数据库配置
        创建数据库连接，并调用相关函数创建数据库（可能包含创建表等操作）
        """
        try:
            await self.database.connect()
            await create_db()
        except Exception as e:
            logger.error(f"数据库初始化失败: {e}")
            raise

    # 服务加载时，调用此函数，用于执行初始化操作
    async def on_init(self):
        """
        服务启动时执行数据库初始化操作的入口点
        """
        await self.init_db()

    # 服务卸载时，调用此函数
    async def on_finalize(self):
        """
        此处可添加服务卸载时的清理操作，比如关闭数据库连接等，目前暂时留空
        """
        pass

    # AddUser
    # 添加用户
    async def add_user(self, ctx: xport_core.Context, param: source_api.UserInfo):
        """
        添加新用户的方法
        在事务中进行操作，先检查用户名是否已存在，若不存在则对密码进行加盐处理后存入数据库
        """
        async with self.engine.begin() as conn:
            try:
                user = await user_dao.find_by_name(conn, param.username)
                if user is not None:
                    raise UserExistsError(f"用户：{param.username} 已经存在")
                salt = gen_random_string()
                md5 = gen_md5(param.password)
                md5_with_salt = gen_md5(md5 + salt)
                await user_dao.add_user(conn, param.username, md5_with_salt, salt)
            except Exception as e:
                logger.error(f"添加用户失败: {e}")
                raise

    # Login
    # 登录 
    async def login(self, ctx: xport_core.Context, param: source_api.UserInfo):
        """
        用户登录方法
        在事务中验证用户名和密码，若登录成功，更新当前登录用户的user_id，并根据记住密码的相关设置更新数据库记录
        """
        async with self.engine.begin() as conn:
            try:
                user_model = await user_dao.find_by_name(conn, param.username)
                if user_model is None:
                    raise UserNotFoundError(f"用户：{param.username} 不存在！")
                md5 = gen_md5(param.password)
                md5 = gen_md5(md5 + user_model.salt)
                if md5.lower()!= user_model.password:
                    raise PasswordError("密码错误！")
                self.user_id = user_model.id
                remember = await remember_dao.find_by_id(conn)
                is_remember = remember is not None
                if param.remember:
                    if is_remember:
                        await remember_dao.update(conn, param.username, param.password)
                    else:
                        await remember_dao.insert(conn, param.username, param.password)
                else:
                    if is_remember:
                        await remember_dao.update(conn, "", "")
            except Exception as e:
                logger.error(f"登录失败: {e}")
                raise

    # IsLogin
    # 用户是否登录
    async def is_login(self, ctx: xport_core.Context) -> source_api.BoolResponse:
        """
        判断当前用户是否已登录，返回相应的布尔响应对象
        """
        resp = source_api.BoolResponse()
        resp.value = self.user_id!= 0
        return resp

    # GetRememberAccount
    # 获取记住的密码
    async def get_remember_account(self, ctx: xport_core.Context) -> source_api.UserInfo:
        """
        获取上次记住的账户信息（用户名和密码）
        """
        async with self.engine.connect() as conn:
            resp = source_api.UserInfo()
            remember = await remember_dao.find_by_id(conn)
            if remember is not None:
                resp.username = remember.username
                resp.password = remember.password
            return resp

    # Logout
    # 退出登录
    async def logout(self, ctx: xport_core.Context):
        """
        实现用户退出登录功能，主要是将当前登录用户的user_id重置为0，并可根据需要执行其他清理操作，比如清除记住密码相关记录等
        """
        try:
            # 重置当前登录用户的user_id
            self.user_id = 0
            async with self.engine.begin() as conn:
                # 此处可以添加代码来清除记住密码相关记录，比如调用remember_dao中的相关方法
                await remember_dao.update(conn, "", "")
            logger.info("用户已成功退出登录")
        except Exception as e:
            logger.error(f"退出登录失败: {e}")
            raise